﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using QuTouTiaoAssist.Entity;
using QuTouTiaoAssist.Util;

namespace QuTouTiaoAssist
{
    public partial class FrmBatchOpe : FrmBase
    {
        private bool m_isBatching = false;
        private List<BatchMemberInfoLite> users = new List<BatchMemberInfoLite>(); 
        private List<BatchOpeItem> opeItems = new List<BatchOpeItem>(); 
        private TimeSpan intervalTime = TimeSpan.Zero;
        private Dictionary<BatchMemberInfoLite, BatchOpeItemResult> opeResults = new Dictionary<BatchMemberInfoLite, BatchOpeItemResult>(); 

        public FrmBatchOpe()
        {
            InitializeComponent();
            InitUserList();
            InitBatchOpeItems();
        }

        private void btnRead_Click(object sender, EventArgs e)
        {
            var users = GetCheckedUsers();
            if (users.Count < 1)
            {
                MsgBox.ShowInfo("请选择用户！");
                return;
            }
            var articleKey = this.txtArticleKey.Text.Trim();
            if (OtherUtil.IsNullOrWhitespace(articleKey))
            {
                MsgBox.ShowInfo("请输入文章Key！");
                return;
            }

            for (int i = 0; i < users.Count; i++)
            {
                ClearUserState(users[i].RowIndex);
            }

            new Thread(() =>
            {
                Random random = new Random((int)DateTime.Now.Ticks);
                for (int i = 0; i < users.Count; i++)
                {
                    var user = users[i];
                    try
                    {
                        var result = QttHelper.ReadArticle(user.MemberId, user.Token, articleKey);

                        if (result.Code == 0)
                        {
                            SetUserStateInfo(user.RowIndex, "成功，+" + result.Data.Amount);
                        }
                        else
                        {
                            throw new Exception(result.Code.ToString());
                        }
                    }
                    catch (Exception ex)
                    {
                        SetUserStateInfo(user.RowIndex, "失败，" + ex.Message);
                    }

                    Thread.Sleep(random.Next(800,1300));
                }
                MsgBox.ShowInfo("完成！");
            }){IsBackground = true}.Start();
        }

        private void btnStartGua_Click(object sender, EventArgs e)
        {
            if (m_isBatching)
            {
                base.SetControlText(this.btnStartGua, "正在停止");
                base.SetControlEnabled(this.btnStartGua, false);
                m_isBatching = false;
                return;
            }

            #region 读取控件数据
            users = GetCheckedUsers();
            if (users.Count < 1)
            {
                MsgBox.ShowInfo("请选择用户！");
                return;
            }
            opeItems = GetCheckedBatchOpeItems();
            if (opeItems.Count < 1)
            {
                MsgBox.ShowInfo("请选择需要刷的项目！");
                return;
            }
            intervalTime = new TimeSpan(
                this.dateTimePicker1.Value.Hour,
                this.dateTimePicker1.Value.Minute,
                this.dateTimePicker1.Value.Second); 
            #endregion

            #region 设置控件状体
            for (int i = 0; i < users.Count; i++)
            {
                ClearUserState(users[i].RowIndex);
            }
            base.SetControlText(this.label3, "下次刷机时间");
            base.SetControlText(this.btnStartGua, "正在挂机"); 
            #endregion

            m_isBatching = true;
            opeResults.Clear();
            for (int i = 0; i < users.Count; i++)
            {
                opeResults.Add(users[i], new BatchOpeItemResult());
            }
            new Thread(() =>
            {
                int shuaTimes = 0;
                while (true)
                {
                    for (int i = 0; i < users.Count; i++)
                    {
                        var user = users[i];
                        var userTotalCoins = 0;
                        var userTotalBalance = 0d;

                        #region 对用户进行刷分
                        for (int j = 0; j < opeItems.Count; j++)
                        {
                            var op = opeItems[j];
                            if (op.ItemFunc != null)
                            {
                                #region 刷分项目
                                try
                                {
                                    var opeResult = op.ItemFunc(user, SetUserStateInfo);
                                    userTotalCoins += opeResult.Coins;
                                    userTotalBalance += opeResult.Balance;
                                }
                                catch (Exception ex)
                                {

                                }
                                #endregion
                            }

                            if (IsNeedStop())
                                return;

                            Thread.Sleep(1000);
                        } 
                        #endregion

                        opeResults[user].Coins += userTotalCoins;
                        opeResults[user].Balance += userTotalBalance;
                        SetUserStateInfo(user.RowIndex, "共获得金币：" + userTotalCoins + "，零钱：" + userTotalBalance);

                        // 显示金币和零钱
                        var memberInfo = QttHelper.GetMemberInfo(user.Token);
                        string strCoin = "", strBalance = "";
                        string strPupilNum = "未知";
                        if (memberInfo.Code == 0)
                        {
                            strCoin += memberInfo.Data.Coin;
                            strBalance += memberInfo.Data.Balance;
                            strPupilNum = memberInfo.Data.PupilNumber.ToString();
                        }
                        strCoin += "(+" + opeResults[user].Coins + ")";
                        strBalance += "(+" + opeResults[user].Balance + ")";
                        SetUserMoneyInfo(user.RowIndex, strCoin, strBalance);
                        SetListViewSubItemText(user.RowIndex, 7, strPupilNum);

                        Thread.Sleep(2000);
                    }

                    #region 设置控件状态

                    shuaTimes++;
                    base.SetControlText(this.label3, string.Format("下次刷机时间[{0}]：\r\n" + (DateTime.Now + intervalTime), shuaTimes));
                    for (int i = 0; i < users.Count; i++)
                    {
                        SetUserStateInfo(users[i].RowIndex, "休眠中");
                    } 
                    #endregion

                    // 进入休眠状态
                    for (int i = 0; i < intervalTime.TotalSeconds; i++)
                    {
                        Thread.Sleep(1000);
                        if (IsNeedStop())
                            return;
                    }
                }
            }){IsBackground = true}.Start();
        }

        private bool IsNeedStop()
        {
            if (!m_isBatching)
            {
                base.SetControlText(this.btnStartGua, "开始挂机");
                base.SetControlEnabled(this.btnStartGua, true);
                base.SetControlText(this.label3, "下次刷机时间");
                for (int i = 0; i < users.Count; i++)
                {
                    SetUserStateInfo(users[i].RowIndex, "未启动");
                    SetUserMoneyInfo(users[i].RowIndex, "未知", "未知");
                    SetListViewSubItemText(users[i].RowIndex,7,"未知");
                }
                StringBuilder result = new StringBuilder();
                result.AppendLine("刷分结果：");
                foreach (KeyValuePair<BatchMemberInfoLite, BatchOpeItemResult> item in opeResults)
                {
                    result.AppendLine(string.Format("{0}获得金币：{1}，零钱：{2}", item.Key.NickName, item.Value.Coins,
                        item.Value.Balance));
                }
                MsgBox.ShowInfo(result.ToString());

                return true;
            }

            return false;
        }

        private void InitUserList()
        {
            this.listView1.Items.Clear();
            var list = Util.ConfigUtil.GetUserInfos();
            for (int i = 0; i < list.Count; i++)
            {
                BatchMemberInfoLite info = new BatchMemberInfoLite(list[i]);
                info.RowIndex = i;
                var newItem = this.listView1.Items.Add(new ListViewItem(new string[]
                {
                    (i+1).ToString(),
                    info.NickName,
                    info.MemberId,
                    "未启动",
                    "未知",
                    "未知",
                    info.Token,
                    "未知",
                }));
                newItem.Tag = info;
                newItem.Checked = true;
            }
        }

        private void InitBatchOpeItems()
        {
            this.checkedListBox1.Items.Add(new BatchOpeItem("签到", SignIn), true);
            this.checkedListBox1.Items.Add(new BatchOpeItem("领红包", ReceiveGift), true);
            this.checkedListBox1.Items.Add(new BatchOpeItem("开宝箱", OpenTreasure), true);
            this.checkedListBox1.Items.Add(new BatchOpeItem("分享到朋友圈", ShareToWeiXin), true);
            this.checkedListBox1.Items.Add(new BatchOpeItem("阅读文章", ReadArticle), true);
            //this.checkedListBox1.Items.Add(new BatchOpeItem("阅读推荐文章", ReadRecommandArticle), true);
        }

        private List<BatchMemberInfoLite> GetCheckedUsers()
        {
            var list = new List<BatchMemberInfoLite>();

            for (int i = 0; i < this.listView1.CheckedItems.Count; i++)
            {
                var item = this.listView1.CheckedItems[i];
                list.Add(item.Tag as BatchMemberInfoLite);
            }

            return list;
        }

        private List<BatchOpeItem> GetCheckedBatchOpeItems()
        {
            var list = new List<BatchOpeItem>();

            for (int i = 0; i < this.checkedListBox1.CheckedItems.Count; i++)
            {
                var item = this.checkedListBox1.CheckedItems[i];
                list.Add(item as BatchOpeItem);
            }

            return list;
        }

        private void SetListViewSubItemText(int rowIndex, int colIndex, string info)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new Action<int, int,string>(SetListViewSubItemText), rowIndex, colIndex, info);
                return;
            }

            this.listView1.Items[rowIndex].SubItems[colIndex].Text = info;
        }

        private void SetUserMoneyInfo(int rowIndex, string conin, string balance)
        {
            SetListViewSubItemText(rowIndex, 4, conin);
            SetListViewSubItemText(rowIndex, 5, balance);
        }

        private void SetUserStateInfo(int rowIndex, string info)
        {
            SetListViewSubItemText(rowIndex, 3, info);
        }

        private void ClearUserState(int rowIndex)
        {
            SetUserStateInfo(rowIndex, string.Empty);
        }

        private BatchOpeItemResult SignIn(BatchMemberInfoLite info, Action<int, string> showFunc)
        {
            var opResult = new BatchOpeItemResult();

            showFunc(info.RowIndex, "签到");
            if (!QttHelper.IsTodaySignIn(info.Token))
            {
                var result = QttHelper.SignIn(info.Token);
                if (result.Code < 0)
                {
                    showFunc(info.RowIndex, "签到失败，" + result.Message);
                }
                else
                {
                    showFunc(info.RowIndex, string.Format("签到成功，获取奖励：{0}，已连续签到：{1}天！",
                        result.Data.Amount,
                        result.Data.ContinuationSignIn));
                    opResult.Coins += result.Data.Amount;
                }
            }
            else
            {
                showFunc(info.RowIndex, "今天已签到！");
            }

            return opResult;
        }

        private BatchOpeItemResult ReceiveGift(BatchMemberInfoLite info, Action<int, string> showFunc)
        {
            var opResult = new BatchOpeItemResult();
            try
            {
                var memberInfo = QttHelper.GetMemberInfo(info.Token);
                if (memberInfo.Data.GiftNotice!=null&&
                    memberInfo.Data.GiftNotice.Id!=null)
                {
                    var result = QttHelper.ReceiveGift(info.Token, memberInfo.Data.GiftNotice.Id);
                    if (result.Code == 0)
                    {
                        showFunc(info.RowIndex, "领取成功！");
                        opResult.Balance += Convert.ToDouble(result.Data.Amount);
                        // 领取后，还存在奖励则继续领取，直到领取失败或奖励全部领取完成
                        if (result.Data.NextId > 0)
                        {
                            var funcResult = ReceiveGift(info, showFunc); // 通过递归调用
                            opResult.Balance += funcResult.Balance;
                        }
                    }
                    else
                    {
                        throw new Exception(result.Message);
                    } 
                }
            }
            catch (Exception ex)
            {
                showFunc(info.RowIndex, "领取失败，" + ex.Message);
            }

            return opResult;
        }

        private BatchOpeItemResult OpenTreasure(BatchMemberInfoLite info, Action<int, string> showFunc)
        {
            var opResult = new BatchOpeItemResult();
            showFunc(info.RowIndex, "开宝箱");
            var treasureInfo = QttHelper.GetTreasureInfo(info.Token);
            if (treasureInfo.IsOpenTreasureAble)
            {
                var result = QttHelper.OpenTreasureBox(info.Token);
                if (result < 0)
                {
                    showFunc(info.RowIndex, "开宝箱失败，" + result);
                }
                else
                {
                    showFunc(info.RowIndex, "开宝箱成功，+" + result);
                    opResult.Coins += result;
                    //coins += result;
                }
            }
            else
            {
                showFunc(info.RowIndex, "宝箱已开过，剩余时间为：" + treasureInfo.LeftTime);
            }

            return opResult;
        }

        private BatchOpeItemResult ShareToWeiXin(BatchMemberInfoLite info, Action<int, string> showFunc)
        {
            var opResult = new BatchOpeItemResult();
            showFunc(info.RowIndex, "分享到朋友圈");
            int n1 = 0;
            for (; ; )
            {
                int shareResult = QttHelper.ShareToWeiXin(info.Token);
                if (shareResult < 0)
                {
                    break; // 已达到上限
                }
                else
                {
                    n1 += shareResult;
                    //coins += shareResult;
                }
            }
            opResult.Coins += n1;
            showFunc(info.RowIndex, "分享到朋友圈，+" + n1);

            return opResult;
        }

        private BatchOpeItemResult ReadArticle(BatchMemberInfoLite info, Action<int, string> showFunc)
        {
            var opResult = new BatchOpeItemResult();
            showFunc(info.RowIndex, "获取频道...");
            var channels = QttHelper.GetDefaultChannelList().Data;
            showFunc(info.RowIndex, "获取频道数：" + channels.Count);
            // 为防止被检测为作弊，此处修改为：
            // 读取随机一个频道中的随机两篇文章，并且阅读间隔采用随机时间
            Random random = new Random((int)DateTime.Now.Ticks);
            var channelIndex = random.Next(0, channels.Count - 1);
            //for (int i = 0; i < channels.Count; i++)
            for (int i = channelIndex; i <= channelIndex; i++)
            {
                showFunc(info.RowIndex, string.Format("阅读频道---{0}", channels[i].Name));
                var articles = QttHelper.GetArticleList(channels[i].Id, 1);
                int successReadNum = 0;
                articles = articles.FindAll(match => match.Url != null).ToList(); // 过滤无效文章
                for (int j = 0; j < 2; j++)
                {
                    var articleIndex = random.Next(0, articles.Count - 1);
                    var result = QttHelper.ReadArticle(info.MemberId, info.Token,
                        articles[articleIndex].Url);
                    if (result.Code == 0)
                    {
                        successReadNum++;
                    }
                    Thread.Sleep(random.Next(1300,2000)); // 随机延时
                }
                opResult.Coins += successReadNum*10;
                showFunc(info.RowIndex, string.Format("阅读频道---{0}，获取金币：{1}", channels[i].Name, successReadNum * 10));
            }

            return opResult;
        }

        private BatchOpeItemResult ReadRecommandArticle(BatchMemberInfoLite info, Action<int, string> showFunc)
        {
            var opResult = new BatchOpeItemResult();

            showFunc(info.RowIndex, "阅读推荐文章");
            var articles = QttHelper.GetRecommandArticleList(info.Token);
            foreach (var article in articles)
            {
                if (article.Url == null) // 跳过无效文章
                    continue;

                var result = QttHelper.ReadArticle(info.MemberId, info.Token,
                    article.Url);
                if (result.Code == 0)
                {
                    opResult.Coins += result.Data.Amount;
                }
                Thread.Sleep(500);
            }

            return opResult;
        }

        private void listView1_MouseUp(object sender, MouseEventArgs e)
        {
            if (m_isBatching)
                return;

            if (e.Button != MouseButtons.Right)
                return;

            ContextMenuStrip cms = new ContextMenuStrip();
            cms.Items.Add("刷新用户列表", null, (o, args) =>
            {
                InitUserList();
            });
            cms.Items.Add("刷新用户状态", null, (o, args) =>
            {
                RefreshUserState();
            });
            cms.Items.Add("全选", null, (o, args) =>
            {
                for (int i = 0; i < this.listView1.Items.Count; i++)
                {
                    this.listView1.Items[i].Checked = true;
                }
            });
            cms.Items.Add("全不选", null, (o, args) =>
            {
                for (int i = 0; i < this.listView1.Items.Count; i++)
                {
                    this.listView1.Items[i].Checked = false;
                }
            });
            cms.Show(this.listView1, e.Location);
        }

        /// <summary>
        /// /刷新所有用户状态
        /// </summary>
        private void RefreshUserState()
        {
            var allUsers = new List<BatchMemberInfoLite>();
            for (var i = 0; i < this.listView1.Items.Count; i++)
            {
                allUsers.Add(this.listView1.Items[i].Tag as BatchMemberInfoLite);
            }

            new Thread(() =>
            {
                for (int i = 0; i < allUsers.Count; i++)
                {
                    var user = allUsers[i];
                    try
                    {
                        var result = QttHelper.GetMemberInfo(user.Token);
                        if (result.Code == 0)
                        {
                            SetUserMoneyInfo(user.RowIndex, result.Data.Coin.ToString(), result.Data.Balance);
                            SetListViewSubItemText(user.RowIndex, 7, result.Data.PupilNumber.ToString());
                        }
                        else
                        {
                            throw new Exception(result.Message);
                        }
                    }
                    catch (Exception ex)
                    {
                        SetListViewSubItemText(user.RowIndex, 3, ex.Message);
                    }
                    Thread.Sleep(new Random((int)DateTime.Now.Ticks).Next(600, 1200));
                }
                MsgBox.ShowInfo("刷新完成！");
            }) { IsBackground = true }.Start();
        }
    }

    class BatchMemberInfoLite : QttMemberInfoLite
    {
        public BatchMemberInfoLite(QttMemberInfoLite info)
        {
            this.NickName = info.NickName;
            this.MemberId = info.MemberId;
            this.Token = info.Token;
        }

        public int RowIndex;
    }

    class BatchOpeItem
    {
        public string Name;
        public Func<BatchMemberInfoLite, Action<int, string>, BatchOpeItemResult> ItemFunc;

        public BatchOpeItem(string name, Func<BatchMemberInfoLite, Action<int, string>, BatchOpeItemResult> func)
        {
            this.Name = name;
            this.ItemFunc = func;
        }

        public override string ToString()
        {
            return Name;
        }
    }

    class BatchOpeItemResult
    {
        public int Coins = 0;
        public double Balance = 0;
    }
}
